import React, {
	FC,
	useCallback,
	useEffect,
	useMemo,
	useRef,
	useState,
} from 'react';
import ResizeObserver from 'resize-observer-polyfill';
import useRefDefaults from '../hooks/use-ref-defaults';
import { BORDER_TOP_BOTTOM } from '../utils/constants';
import { addUnit } from '../utils/format';
import { Dictionary } from '../utils/interface';
import { classnames } from '../utils/namespace';
import { TabMeta } from './hooks/use-tab-meta-list';
import { TabsComponentProps, TabsIndicator } from './interface';
import { scrollLeftTo, _bem as bem } from './utils';

interface CProps {
	transitionDuration: number;
	activeIndex: number;
	scrollable: boolean;
	ellipsis: boolean;
	indicator: TabsIndicator;
	metaList: TabMeta[];
	trackWidth?: string | number;
	trackHeight?: string | number;
	trackColor?: string;
	border?: boolean;
	onChange: (key: string) => void;
	onNavItemClick?: TabsComponentProps['onNavItemClick'];
}

const TabsHeader: FC<CProps> = ({
	activeIndex,
	scrollable,
	ellipsis,
	transitionDuration,
	trackColor,
	indicator,
	border,
	metaList,
	onChange,
	...props
}) => {
	const transitionDurationRef = useRefDefaults(transitionDuration);
	const activeIndexRef = useRefDefaults(activeIndex);

	const navListRef = useRef<HTMLDivElement>(null);
	const navItemRefs = useRef<Dictionary<HTMLDivElement | null>>({});

	const scrollTimerRef = useRef<() => void>();

	const [initialized, setInitialized] = useState(false);
	const initializedRef = useRefDefaults(initialized);

	const trackWidth = useMemo(
		() => addUnit(props.trackWidth),
		[props.trackWidth],
	);
	const trackHeight = useMemo(
		() => addUnit(props.trackHeight),
		[props.trackHeight],
	);

	const [trackLeft, setTrackLeft] = useState(0);

	const scrollTo = useCallback(
		(index: number, options?: { immediate?: boolean }) => {
			const navItem = navItemRefs.current[index];
			const navList = navListRef.current;

			if (navItem == null || navList == null) {
				return;
			}

			const immediate = options && options.immediate;

			setTrackLeft(navItem.offsetLeft + navItem.offsetWidth / 2);

			// cancel scroll animate
			if (scrollTimerRef.current) {
				scrollTimerRef.current();
				scrollTimerRef.current = undefined;
			}

			const to =
				navItem.offsetLeft -
				(navList.offsetWidth - navItem.offsetWidth) / 2;

			scrollTimerRef.current = scrollLeftTo(
				navList,
				to,
				immediate ? 0 : transitionDurationRef.current,
			);
		},
		[transitionDurationRef],
	);

	useEffect(() => {
		setInitialized(true);
		scrollTo(activeIndex, { immediate: !initializedRef.current });
	}, [activeIndex, initializedRef, scrollTo]);

	useEffect(() => {
		const navList = navListRef.current;

		let observer: ResizeObserver | undefined;

		if (navList) {
			observer = new ResizeObserver(() => {
				scrollTo(activeIndexRef.current, { immediate: true });
			});

			observer.observe(navList);
		}

		return () => {
			observer && observer.disconnect();
		};
	}, [activeIndexRef, scrollTo]);

	return (
		<div
			className={classnames(
				bem('header'),
				border ? BORDER_TOP_BOTTOM : undefined,
			)}
		>
			<div className={bem('nav')}>
				{indicator.navLeft && (
					<div className={bem('nav-left')}>{indicator.navLeft}</div>
				)}
				<div
					className={bem('nav-list', { scrollable })}
					role="tablist"
					ref={navListRef}
				>
					{metaList.map((meta, idx) => {
						return (
							<div
								key={meta.key}
								role="tab"
								aria-selected={idx === activeIndex}
								className={bem('nav-item', {
									disabled: meta.disabled,
									active:
										idx === activeIndex && !meta.disabled,
								})}
								onClick={(e) => {
									if (props.onNavItemClick) {
										props.onNavItemClick(e, {
											disabled: meta.disabled,
											index: idx,
											key: meta.key,
										});
									}

									if (!meta.disabled && activeIndex !== idx) {
										onChange(meta.key);
									}
								}}
								ref={(ref) => {
									navItemRefs.current[idx] = ref;
								}}
							>
								<div
									className={bem('nav-item-title', {
										ellipsis: !scrollable && ellipsis,
									})}
								>
									{meta.title}
								</div>
							</div>
						);
					})}
					{initialized && (
						<div
							className={bem('nav-track')}
							style={{
								background: trackColor,
								height: trackHeight,
								width: trackWidth,
								left: trackLeft,
								transitionDuration: `${transitionDuration}ms`,
							}}
						/>
					)}
				</div>
				{indicator.navRight && (
					<div className={bem('nav-right')}>{indicator.navRight}</div>
				)}
			</div>
			{indicator.navBottom}
		</div>
	);
};

TabsHeader.displayName = 'TabsHeader';

export default TabsHeader;
